# -*- coding: UTF-8 -*-
# 2023/8/7 22:56
import os
import sys
import time
from PySide6.QtGui import QPixmap, QTextCursor, QShortcut, QKeySequence
from PySide6.QtWidgets import QApplication, QMainWindow, QHBoxLayout, QVBoxLayout, QWidget, QDialog, QLabel, QTextBrowser, QMessageBox, QSlider, QProgressBar, QFileDialog
from PySide6.QtCore import Qt as QtCoreqt
from PySide6.QtGui import Qt as Qtguiqt
from mainwindow_ui import Ui_mainWindow
from text_to_img_ui import Ui_Form
from dialog_ui import Ui_Dialog
import image_rc  # type: ignore
from child_thread import thread_requests_message, thread_requests_token, thread_refresh_progressbar, thread_get_to_img, logger, conf_writer
from requests_message import send_requests


class text_to_img(QWidget, Ui_Form):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.token = None
        self.body = dict()

        self.initui()

    def initui(self):
        # 设置窗口图标
        self.setWindowIcon(QPixmap(":/main/robot"))

        # 设置同步显示当前值
        self.steps_horizontalSlider.valueChanged.connect(lambda: self.steps_label.setText(str(self.steps_horizontalSlider.value())))
        self.n_horizontalSlider.valueChanged.connect(lambda: self.n_label.setText(str(self.n_horizontalSlider.value())))

        self.steps_label.setText(str(self.steps_horizontalSlider.value()))
        self.n_label.setText(str(self.n_horizontalSlider.value()))

        # 设置保存目录回调
        self.select_save_pushButton.clicked.connect(self.select_save_path)
        self.save_path_label.setText(os.getcwd())

        # 文生图按钮回调
        self.thread_get_to_img = thread_get_to_img(self.body, self.token)
        self.to_img_pushButton.clicked.connect(self.get_to_img_clicked)
        # requests处理完请求保存回调
        self.thread_get_to_img.img_result_signal.connect(self.to_img_callback)

        # 初始化默认保存目录
        self.save_path_label.setText(os.getcwd())

    def select_save_path(self):
        """
        选择生成文件保存目录
        """
        self.file_save_path = QFileDialog.getExistingDirectory(caption='选择保存路径', dir=os.path.abspath('.'))
        if self.file_save_path != "":
            self.save_path_label.setText(self.file_save_path)
            logger.info(f"设置文生图保存目录:{self.file_save_path}")

        self.thread_get_to_img.save_path = self.file_save_path

    def get_to_img_clicked(self):
        """
        生成图片回调
        """
        run_continue = self.get_img_parameter()  # 更新参数
        if not run_continue:
            QMessageBox.warning(self, "未填写prompt参数", "未填写prompt参数,请填写后再次尝试", QMessageBox.StandardButton.Ok)
            return

        self.to_img_pushButton.setEnabled(False)  # 防止重复点击隐藏
        self.thread_get_to_img.start()  # 子线程更新

    def to_img_callback(self, img_list: list):
        """
        请求生成图像后回调,处理并保存图像
        :param img_list: 信号返回的图像列表
        """
        logger.info(f"处理完成,处理图像数量:{len(img_list)}")

        self.to_img_pushButton.setEnabled(True)

    def get_img_parameter(self):
        """
        获取发起请求的参数,更新self.body
        """
        self.thread_get_to_img.access_token = self.token

        __body = dict()
        prompt = self.prompt_lineEdit.text()
        negative_prompt = self.negative_prompt_lineEdit.text()
        n = self.n_horizontalSlider.value()
        steps = self.steps_horizontalSlider.value()
        sampler_index = self.get_sampler_index()
        size = self.get_size()

        logger.info(f"本次请求参数prompt:{prompt}、negative_prompt:{negative_prompt}、size:{size}、n:{n}、sampler_index:{sampler_index}、:{steps}")

        if prompt != "":
            __body["prompt"] = prompt
        else:
            logger.warning(f"prompt必填")
            return False

        if negative_prompt != "":
            __body["negative_prompt"] = negative_prompt

        __body["size"] = size
        __body["steps"] = int(steps)
        __body["n"] = int(n)
        __body["sampler_index"] = sampler_index

        self.body = __body
        self.thread_get_to_img.body = __body

        return True

    def get_size(self):
        """
        获取size参数并返回
        :return:
        """
        size_but = [self.sizeButton_1, self.sizeButton_2, self.sizeButton_3, self.sizeButton_4, self.sizeButton_5, self.sizeButton_6]
        for btn in size_but:
            if btn.isChecked():
                return btn.text()

    def get_sampler_index(self):
        """
        获取sampler_index参数并返回
        :return:
        """
        sampler_index_bun = [self.sampler_1, self.sampler_2, self.sampler_3, self.sampler_4, self.sampler_5,
                             self.sampler_6, self.sampler_7, self.sampler_8, self.sampler_9, self.sampler_10,
                             self.sampler_11, self.sampler_12, self.sampler_13, self.sampler_14, self.sampler_15]

        for btn in sampler_index_bun:
            if btn.isChecked():
                return btn.text()


class model_dialog(QDialog, Ui_Dialog):
    def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.initui()

    def initui(self):
        self.setWindowIcon(QPixmap(":/main/robot"))

        # 读取配置更新至组件
        self.model_comboBox.setCurrentText(conf_writer["model_dialog"]["model"])

        self.temperature_checkBox.setChecked(eval(conf_writer["model_dialog"]["temperature_checkBox"]))
        self.temperature_Slider.setValue(float(conf_writer["model_dialog"]["temperature_value"]))
        self.temperature_lcdNumber.display(float(conf_writer["model_dialog"]["temperature_value"]))

        self.top_p_checkBox.setChecked(eval(conf_writer["model_dialog"]["top_p_checkBox"]))
        self.top_p_Slider.setValue(float(conf_writer["model_dialog"]["top_p_value"]))
        self.top_p_lcdNumber.display(float(conf_writer["model_dialog"]["top_p_value"]))

        self.penalty_score_checkBox.setChecked(eval(conf_writer["model_dialog"]["penalty_score_checkBox"]))
        self.penalty_score_Slider.setValue(float(conf_writer["model_dialog"]["penalty_score_value"]))
        self.penalty_score_lcdNumber.display(float(conf_writer["model_dialog"]["penalty_score_value"]))

        self.top_k_checkBox.setChecked(eval(conf_writer["model_dialog"]["top_k_checkBox"]))
        self.top_k_spinBox.setValue(int(conf_writer["model_dialog"]["top_k_value"]))

        self.disable_search_checkBox.setChecked(eval(conf_writer["model_dialog"]["disable_search_checkBox"]))
        self.enable_citation_checkBox.setChecked(eval(conf_writer["model_dialog"]["enable_citation_checkBox"]))

        self.stop_checkBox.setChecked(eval(conf_writer["model_dialog"]["stop_checkBox"]))
        self.stop_lineEdit.setText(conf_writer["model_dialog"]["stop"])

        self.system_checkBox.setChecked(eval(conf_writer["model_dialog"]["system_checkBox"]))
        self.system_lineEdit.setText(conf_writer["model_dialog"]["system_text"])

        # 更新对应状态
        self.updata_state()

        # 更新模型选值,更新配置
        self.model_comboBox.currentIndexChanged.connect(self.updata_state)

        # 滑块状态联动
        self.model_comboBox.currentIndexChanged.connect(self.updata_state)
        self.temperature_checkBox.stateChanged.connect(self.updata_state)
        self.top_p_checkBox.stateChanged.connect(self.updata_state)
        self.penalty_score_checkBox.stateChanged.connect(self.updata_state)
        self.system_checkBox.stateChanged.connect(self.updata_state)

        # 设置三个滑块取值范围
        self.temperature_Slider.setMinimum(1)  # 100倍
        self.temperature_Slider.setMaximum(100)
        self.temperature_Slider.setValue(95)  # 默认值
        self.temperature_Slider.setSingleStep(1)  # 单步步长以及方向按键步长
        self.temperature_Slider.setPageStep(3)  # 翻页步长
        self.temperature_Slider.setTickPosition(QSlider.TickPosition.TicksAbove)  # 刻度线位置
        self.temperature_Slider.setTickInterval(1)  # 刻度间隔

        self.top_p_Slider.setMinimum(0)  # 10倍
        self.top_p_Slider.setMaximum(10)
        self.top_p_Slider.setValue(80)
        self.top_p_Slider.setSingleStep(1)
        self.top_p_Slider.setPageStep(3)
        self.top_p_Slider.setTickPosition(QSlider.TickPosition.TicksAbove)
        self.top_p_Slider.setTickInterval(1)

        self.penalty_score_Slider.setMinimum(10)  # 10倍
        self.penalty_score_Slider.setMaximum(20)
        self.penalty_score_Slider.setValue(10)
        self.penalty_score_Slider.setSingleStep(1)
        self.penalty_score_Slider.setPageStep(3)
        self.penalty_score_Slider.setTickPosition(QSlider.TickPosition.TicksAbove)
        self.penalty_score_Slider.setTickInterval(1)

        # 设置三个lcd显示区间
        self.temperature_lcdNumber.setDigitCount(4)
        self.top_p_lcdNumber.setDigitCount(3)
        self.penalty_score_lcdNumber.setDigitCount(3)

        # 设置三个滑块同步关联lcd显示
        self.temperature_Slider.valueChanged.connect(self.temperature_Slider_change)
        self.top_p_Slider.valueChanged.connect(self.top_p_Slider_change)
        self.penalty_score_Slider.valueChanged.connect(self.penalty_score_Slider_change)

        # 当人设文字更新同步配置
        self.system_lineEdit.textChanged.connect(self.updata_state)

    def temperature_Slider_change(self, value: int):
        self.temperature_lcdNumber.display(value / 100)

    def top_p_Slider_change(self, value: int):
        self.top_p_lcdNumber.display(value / 10)

    def penalty_score_Slider_change(self, value: int):
        self.penalty_score_lcdNumber.display(value / 10)

    def updata_state(self):
        """
        根据选择模型更新参数组件状态,会在最后调用更新配置文件
        """

        def set_system_topk_states(status: bool):
            """
            分别设置system/top_k几个组件状态
            """
            self.system_checkBox.setEnabled(status)
            self.system_lineEdit.setEnabled(status)
            self.top_k_checkBox.setEnabled(status)
            self.top_k_spinBox.setEnabled(status)

        def set_stop_states(status: bool):
            """
            设置stop组件状态
            """
            self.stop_checkBox.setEnabled(status)
            self.stop_lineEdit.setEnabled(status)

        def set_6_states(status: bool):
            """
            设置disable_search和enable_citation组件状态
            """
            self.disable_search_checkBox.setEnabled(status)
            self.enable_citation_checkBox.setEnabled(status)

        # ["Llama-2-7b-chat", "Llama-2-13b-chat", "Llama-2-70b-chat", "Qianfan_Chinese_Llama_2_7B", "Qianfan_BLOOMZ_7B_compressed", "ChatGLM2_6B_32K", "AquilaChat_7B"]:
        if self.select_model != "ERNIE_Bot_turbo":  # 除了ERNIE_Bot_tur都有stop参数
            self.stop_checkBox.setEnabled(True)
            self.stop_lineEdit.setEnabled(True)
        else:
            self.stop_checkBox.setEnabled(False)
            self.stop_lineEdit.setEnabled(False)

        if self.select_model in ["ERNIE_Bot_turbo", "ERNIE_Bot", "ERNIE_Bot_4"]:  # 只有这三个有system参数,同时只有这三个没有top_k参数
            self.system_checkBox.setEnabled(True)
            self.system_lineEdit.setEnabled(True)
            self.top_k_checkBox.setEnabled(False)
            self.top_k_spinBox.setEnabled(False)
        else:
            self.system_checkBox.setEnabled(False)
            self.system_lineEdit.setEnabled(False)
            self.top_k_checkBox.setEnabled(True)
            self.top_k_spinBox.setEnabled(True)

        if self.select_model in ["ERNIE_Bot", "ERNIE_Bot_4"]:  # 只有"ERNIE_Bot", "ERNIE_Bot_4"参数会有disable_search和enable_citation两个bool
            self.disable_search_checkBox.setEnabled(True)
            self.enable_citation_checkBox.setEnabled(True)
        else:
            self.disable_search_checkBox.setEnabled(False)
            self.enable_citation_checkBox.setEnabled(False)

        self.update_conf()

    def update_conf(self):
        """
        更新配置文件,注意初始化参数在主窗口类的init_config下生成
        此处只是更新
        """
        conf_writer["model_dialog"]["model"] = str(self.model_comboBox.currentText())

        conf_writer["model_dialog"]["temperature_checkBox"] = True if self.temperature_checkBox.checkState() == QtCoreqt.CheckState.Checked else False
        conf_writer["model_dialog"]["temperature_value"] = self.temperature_value

        conf_writer["model_dialog"]["top_p_checkBox"] = True if self.top_p_checkBox.checkState() == QtCoreqt.CheckState.Checked else False
        conf_writer["model_dialog"]["top_p_value"] = self.top_p_value

        conf_writer["model_dialog"]["penalty_score_checkBox"] = True if self.penalty_score_checkBox.checkState() == QtCoreqt.CheckState.Checked else False
        conf_writer["model_dialog"]["penalty_score_value"] = self.penalty_score_value

        conf_writer["model_dialog"]["top_k_checkBox"] = True if self.top_k_checkBox.checkState() == QtCoreqt.CheckState.Checked else False
        conf_writer["model_dialog"]["top_k_value"] = self.top_k_value

        conf_writer["model_dialog"]["disable_search_checkBox"] = True if self.disable_search_checkBox.checkState() == QtCoreqt.CheckState.Checked else False

        conf_writer["model_dialog"]["enable_citation_checkBox"] = True if self.enable_citation_checkBox.checkState() == QtCoreqt.CheckState.Checked else False

        conf_writer["model_dialog"]["stop_checkBox"] = True if self.stop_checkBox.checkState() == QtCoreqt.CheckState.Checked else False
        conf_writer["model_dialog"]["stop"] = self.stop_lineEdit.text()

        conf_writer["model_dialog"]["system_checkBox"] = True if self.system_checkBox.checkState() == QtCoreqt.CheckState.Checked else False
        conf_writer["model_dialog"]["system_text"] = self.system_value

        with open('config.ini', 'wb') as ini_file:
            conf_writer.write(ini_file)

        logger.info(f"模型窗口配置文件更新:"
                    f"\n\tmodel:{conf_writer['model_dialog']['model']}"
                    f"\n\ttemperature_checkBox:{conf_writer['model_dialog']['temperature_checkBox']}"
                    f"\n\ttemperature_value:{conf_writer['model_dialog']['temperature_value']}"
                    f"\n\ttop_p_checkBox:{conf_writer['model_dialog']['top_p_checkBox']}"
                    f"\n\ttop_p_value:{conf_writer['model_dialog']['top_p_value']}"
                    f"\n\tpenalty_score_checkBox :{conf_writer['model_dialog']['penalty_score_checkBox']}"
                    f"\n\tpenalty_score_value:{conf_writer['model_dialog']['penalty_score_value']}"
                    f"\n\ttop_k_checkBox:{conf_writer['model_dialog']['top_k_checkBox']}"
                    f"\n\ttop_k_value:{conf_writer['model_dialog']['top_k_value']}"
                    f"\n\tdisable_search_checkBox :{conf_writer['model_dialog']['disable_search_checkBox']}"
                    f"\n\tenable_citation_checkBox:{conf_writer['model_dialog']['enable_citation_checkBox']}"
                    f"\n\tstop_checkBox:{conf_writer['model_dialog']['stop_checkBox']}"
                    f"\n\tstop:{conf_writer['model_dialog']['stop']}"
                    f"\n\tsystem_checkBox :{conf_writer['model_dialog']['system_checkBox']}"
                    f"\n\tsystem_text :{conf_writer['model_dialog']['system_text']}")

    @property
    def select_model(self):
        """
        获取当前选中的模型
        """
        return str(self.model_comboBox.currentText())

    @property
    def temperature_value(self):
        """
        temperature float
        - 较高的数值会使输出更加随机，而较低的数值会使其更加集中和确定
        - 默认0.95，范围 (0, 1.0]，不能为0
        - 建议该参数和top_p只设置1个
        - 建议top_p和temperature不要同时更改
        """
        return self.temperature_lcdNumber.value()

    @property
    def top_p_value(self):
        """
        top_p float
        - 影响输出文本的多样性，取值越大，生成文本的多样性越强
        - 默认0.8，取值范围 [0, 1.0]
        - 建议该参数和temperature只设置1个
        - 建议top_p和temperature不要同时更改
        """
        return self.top_p_lcdNumber.value()

    @property
    def penalty_score_value(self):
        """
        penalty_score float 通过对已生成的token增加惩罚，减少重复生成的现象。说明：
        - 值越大表示惩罚越大
        - 默认1.0，取值范围：[1.0, 2.0]
        """
        return self.penalty_score_lcdNumber.value()

    @property
    def system_value(self):
        """
        system string 模型人设，主要用于人设设定，例如，你是xxx公司制作的AI助手，说明：
        - 长度限制1024个字符
        - 如果使用functions参数，不支持设定人设system
        """
        return self.system_lineEdit.text()

    @property
    def stop_value(self):
        """
        stop生成停止标识。当模型生成结果以stop中某个元素结尾时，停止文本生成。说明：
        (1)每个元素长度不超过20字符。
        (2)最多4个元素
        (3)多个元素请以双逗号分割
        (4)错误格式导致解析错误无法解析将会不处理此参数
        """
        stop_text = self.stop_lineEdit.text()
        if stop_text == "":
            return list()

        stop_text = stop_text.replace("，，", ",,")
        stop = stop_text.split(",,")

        if len_stop := len(stop) > 4:
            logger.warning(f"停止标识列表最多4个元素,本次解析结果个数:{len_stop}")
            return list()

        for i in stop:  # 单个元素不能超过20字符
            if len(i) > 20:
                logger.warning(f"停止标识列表单个元素不能超过20字符,其中解析出以下不符合标准:{i}")
                return list()
        logger.info(f"最终解析stop参数{stop}")
        return stop

    @property
    def top_k_value(self):
        """
        Top-K 采样参数，在每轮token生成时，保留k个概率最高的token作为候选。说明：
        (1)影响输出文本的多样性，取值越大，生成文本的多样性越强
        (2)取值范围：正整数
        """
        return self.top_k_spinBox.value()


class MainWindow(QMainWindow, Ui_mainWindow):
    def __init__(self):
        super().__init__()

        self.setupUi(self)

        self.access_token = None
        self.talk_list = list()  # 连续对话列表
        self.request_kwargs = dict()
        self.user_id = str(time.time())

        self.init_config()
        self.initui()

    def init_config(self):
        """
        初始化配置文件
        """
        if not os.path.exists("./config.ini"):
            # 初始化配置文件
            conf_writer["model_dialog"] = dict()
            conf_writer["main"] = dict()

            # 初始化主窗口默认参数
            conf_writer["main"]["API_Key"] = "1mlZDxhxnVedAzezUASLXOK22"
            conf_writer["main"]["Secret_Key"] = "PGDGbsn6PVTD5SdxgcTTIK3X7gs8Gtxq"
            conf_writer["main"]["continuous_talk"] = "False"

            # 初始化模型窗口的参数
            conf_writer["model_dialog"]["model"] = "ERNIE_Bot_4"
            conf_writer["model_dialog"]["temperature_checkBox"] = "False"
            conf_writer["model_dialog"]["temperature_value"] = 0.95
            conf_writer["model_dialog"]["top_p_checkBox"] = "False"
            conf_writer["model_dialog"]["top_p_value"] = 0.8
            conf_writer["model_dialog"]["penalty_score_checkBox"] = "False"
            conf_writer["model_dialog"]["penalty_score_value"] = 1.0
            conf_writer["model_dialog"]["top_k_checkBox"] = "False"
            conf_writer["model_dialog"]["top_k_value"] = 1
            conf_writer["model_dialog"]["disable_search_checkBox"] = "False"
            conf_writer["model_dialog"]["enable_citation_checkBox"] = "False"
            conf_writer["model_dialog"]["stop_checkBox"] = "False"
            conf_writer["model_dialog"]["stop"] = ""
            conf_writer["model_dialog"]["system_checkBox"] = "False"
            conf_writer["model_dialog"]["system_text"] = ""

            # 写入配置
            with open('config.ini', 'wb') as ini_file:
                conf_writer.write(ini_file)

        # 提前定义几个关键参数,方式后面no attribute
        self.API_Key = conf_writer["main"]["API_Key"]
        self.Secret_Key = conf_writer["main"]["Secret_Key"]
        talk_states = conf_writer["main"]["continuous_talk"]
        self.continuous_talk_state = QtCoreqt.CheckState.Checked if eval(talk_states) else QtCoreqt.CheckState.Unchecked

    def initui(self):
        # 独立线程更新token
        self.thread_token = thread_requests_token(self.API_Key, self.Secret_Key)
        self.thread_token.token_singnal.connect(self.refresh_token)
        self.thread_token.start()

        self._requests = send_requests()  # 实例化请求类
        self.model_dialog = model_dialog()  # 实例化模型设置对话框
        self.text_to_img_widget = text_to_img()  # 实例化模型设置对话框

        self.setWindowIcon(QPixmap(":/main/robot"))  # 设置窗口图标

        # 设置模型按钮弹出模型配置对话框
        self.model_pushButton.clicked.connect(lambda: self.model_dialog.exec())

        # 图生文按钮回调
        self.text_to_img_pushButton.clicked.connect(lambda: self.text_to_img_widget.show())

        # 回填默认api key和Secret Key到两个输入框
        self.apikey_lineEdit.setText(self.API_Key)
        self.Secretkey_lineEdit.setText(self.Secret_Key)
        self.continuous_checkBox.setCheckState(self.continuous_talk_state)  # 根据配置设置连续对话初始状态
        logger.info(f"通过配置文件读取主窗口配置:\n\tAPI_Key:{self.API_Key}\n\tSecret_Key:{self.Secret_Key}\n\tcontinuous_talk_state:{self.continuous_talk_state}")

        # 配置滚动容器
        self.scrollArea_widget = QWidget()  # 滚动区域中的widget组件
        self.talk_scrollArea.setWidget(self.scrollArea_widget)  # 设置滚动区域显示的组件为widget
        self.talk_scrollArea_v_ScrollBar = self.talk_scrollArea.verticalScrollBar()  # 垂直滚动条对象

        # 定义widget组件的中心布局器为垂直布局
        self.scrollArea_v_layout = QVBoxLayout()
        self.scrollArea_v_layout.setAlignment(QtCoreqt.AlignmentFlag.AlignTop | QtCoreqt.AlignmentFlag.AlignLeft)  # 顶部对齐
        self.scrollArea_widget.setLayout(self.scrollArea_v_layout)

        # 给self.send_plainTextEdit提示框设置快捷键和对应方法
        self.send_shortcutkeys = QShortcut(QKeySequence("Ctrl+Alt+S"), self.send_plainTextEdit)
        self.send_shortcutkeys.activated.connect(self.send_message_slot)

        # 进度条组件
        self.talk_qprogressbar = QProgressBar()
        self.talk_qprogressbar.setRange(1, 100)  # 进度条值范围
        self.talk_qprogressbar.setValue(100)
        self.talk_qprogressbar.setTextVisible(False)  # 进度条不显示值
        self.talk_qprogressbar.setOrientation(QtCoreqt.Orientation.Horizontal)  # 进度条方向
        self.verticalLayout_3.addWidget(self.talk_qprogressbar)
        # 将进度条请求方法放到独立线程
        self.refresh_progressbar = thread_refresh_progressbar()
        self.refresh_progressbar.refreshSignal.connect(self.refresh_progressbar_slot)

        # 将请求回复消息连接到receive_message槽函数
        self.tread_send_message = thread_requests_message(self._requests, self.talk_list, self.model_dialog.select_model, self.access_token, **self.request_kwargs)
        self.tread_send_message.answer_singnal.connect(self.receive_message_slot)

        # 连续对话开关切换状态时重置连续对话列表
        self.continuous_checkBox.stateChanged.connect(self.continuous_checkBox_stateChanged_slot)

        # 当模型改变,参数开关,参数选择值更新时回调槽函数
        self.request_kwargs_slot()  # 初始化请求参数
        self.model_dialog.model_comboBox.currentIndexChanged.connect(self.request_kwargs_slot)  # 模型改变
        self.model_dialog.temperature_checkBox.stateChanged.connect(self.request_kwargs_slot)  # temperature开关更新
        self.model_dialog.top_p_checkBox.stateChanged.connect(self.request_kwargs_slot)  # top_p开关更新
        self.model_dialog.temperature_Slider.valueChanged.connect(self.request_kwargs_slot)  # 值更新
        self.model_dialog.top_p_Slider.valueChanged.connect(self.request_kwargs_slot)  # top_p值更新
        self.model_dialog.penalty_score_Slider.valueChanged.connect(self.request_kwargs_slot)  # penalty_score值更新

        # 更新API_Key和Secret_Key时绑定槽函数
        self.apikey_lineEdit.textChanged.connect(self.change_apikey_secretkey_slot)
        self.Secretkey_lineEdit.textChanged.connect(self.change_apikey_secretkey_slot)

    @property
    def continuous_talk(self):
        """
        获取连续对话开关状态
        """
        if self.continuous_checkBox.checkState() == QtCoreqt.CheckState.Checked:
            return True
        else:
            return False

    @property
    def get_apikey(self):
        return self.apikey_lineEdit.text()

    @property
    def get_secretkey(self):
        return self.Secretkey_lineEdit.text()

    def change_apikey_secretkey_slot(self):
        """
        更新API_Key和Secret_Key时回调
        """
        self.API_Key = self.get_apikey
        self.Secret_Key = self.get_secretkey
        self.update_conf(self.API_Key, self.Secret_Key)

        self.thread_token.API_Key = self.API_Key
        self.thread_token.Secret_Key = self.Secret_Key
        _token = self._requests.get_access_token(self.API_Key, self.Secret_Key)
        self.refresh_token(_token)
        logger.info(f"更新API_Key或Secret_Key全局token同步刷新:{_token}")

    def refresh_token(self, token: str):
        """
        更新全局token
        请求子线程token在self.request_kwargs_slot更新
        """
        self.access_token = token
        self.token_statusbar.showMessage(token)
        self.text_to_img_widget.token = token  # 更新文生图的token变量

        logger.info(f"更新全局token和状态栏显示token:{token}")

    def update_conf(self, API_Key=None, Secret_Key=None, continuous_talk=None):
        """
        更新配置文件,API_Key/Secret_Key/model不传值则走默认生成,开关类默认False
        """
        if API_Key:
            conf_writer["main"]["API_Key"] = API_Key

        if Secret_Key:
            conf_writer["main"]["Secret_Key"] = Secret_Key

        if continuous_talk is not None:
            conf_writer["main"]["continuous_talk"] = str(continuous_talk)

        if any([API_Key, Secret_Key, continuous_talk]):
            with open('config.ini', 'wb') as ini_file:
                conf_writer.write(ini_file)

        logger.info(f"主窗口配置更新:API_Key:{API_Key}/n\tSecret_Key:{Secret_Key}/n\tcontinuous_talk:{continuous_talk}")

    def request_kwargs_slot(self):
        """
        当模型改变,参数开关,参数选择值更新时回调槽函数
        """
        _request_kwargs = dict()
        _request_kwargs['user_id'] = str(time.time())  # user_id对象

        if self.model_dialog.temperature_checkBox.checkState() == QtCoreqt.CheckState.Checked:  # temperature值
            _request_kwargs["temperature"] = self.model_dialog.temperature_value

        if self.model_dialog.top_p_checkBox.checkState() == QtCoreqt.CheckState.Checked:  # top_p值
            _request_kwargs["top_p"] = self.model_dialog.top_p_value

        if self.model_dialog.penalty_score_checkBox.checkState() == QtCoreqt.CheckState.Checked:  # penalty_score值
            _request_kwargs["penalty_score"] = self.model_dialog.penalty_score_value

        if self.model_dialog.top_k_checkBox.checkState() == QtCoreqt.CheckState.Checked and self.model_dialog.top_k_spinBox.isEnabled():  # top_k值
            _request_kwargs["top_k"] = self.model_dialog.top_k_value

        if self.model_dialog.disable_search_checkBox.checkState() == QtCoreqt.CheckState.Checked and self.model_dialog.disable_search_checkBox.isEnabled():  # disable_search值
            _request_kwargs["disable_search"] = True if self.model_dialog.disable_search_checkBox.checkState() == QtCoreqt.CheckState.Checked else False

        if self.model_dialog.enable_citation_checkBox.checkState() == QtCoreqt.CheckState.Checked and self.model_dialog.enable_citation_checkBox.isEnabled():  # disable_search值
            _request_kwargs["enable_citation"] = True if self.model_dialog.enable_citation_checkBox.checkState() == QtCoreqt.CheckState.Checked else False

        if self.model_dialog.stop_checkBox.checkState() == QtCoreqt.CheckState.Checked and self.model_dialog.stop_lineEdit.isEnabled():  # disable_search值
            _request_kwargs["enable_citation"] = self.model_dialog.stop_value

        if self.model_dialog.system_checkBox.checkState() == QtCoreqt.CheckState.Checked:  # top_p值
            _request_kwargs["system"] = self.model_dialog.system_value  # system人设值

        self.request_kwargs = _request_kwargs

        # 更新请求方法里的参数
        self.tread_send_message.kwargs = _request_kwargs
        self.tread_send_message.talk_list = self.talk_list
        self.tread_send_message.model = self.model_dialog.select_model
        self.tread_send_message.access_token = self.access_token

        logger.info(f"更新子线程请求参数 \n\tkwargs：{_request_kwargs}\n\tmodel:{self.tread_send_message.model}\n\taccess_token:{self.access_token}\n\ttalk_list:{self.talk_list}")

    def continuous_checkBox_stateChanged_slot(self, state):
        """
        连续对话按钮更新状态时回调,重置对话列表,防止请求异常
        """
        self.talk_list = list()
        logger.info(f"连续对话按钮选择状态更新为{self.continuous_checkBox.checkState()}  {state}, 清空对话列表")

        if state == 2:
            self.continuous_talk_state = QtCoreqt.CheckState.Checked
            self.update_conf(continuous_talk=True)

        else:
            self.continuous_talk_state = QtCoreqt.CheckState.Unchecked
            self.update_conf(continuous_talk=False)

    def refresh_progressbar_slot(self):
        """
        更新进度条回调
        """
        if self.talk_qprogressbar.value() == 100:
            self.talk_qprogressbar.reset()

        self.talk_qprogressbar.setValue(self.talk_qprogressbar.value() + 10)

    def send_message_slot(self):
        """
        发送提问消息时回调槽函数
        """
        if not self.get_apikey:
            QMessageBox.warning(self, "未输入api key", "api key不能为空,请正确输入api key！", QMessageBox.StandardButton.Ok)
            logger.warning(f"未填写api key,阻止请求")
            return

        if not self.get_secretkey:
            QMessageBox.warning(self, "未输入secret key", "secret key不能为空,请正确输入secret key！", QMessageBox.StandardButton.Ok)
            logger.warning(f"未填写secret key,阻止请求")
            return

        if not self.access_token:
            QMessageBox.warning(self, "未获取到token", "当前token为空,请正确填写api key和secret key并获取到token后再次尝试", QMessageBox.StandardButton.Ok)
            logger.warning(f"未获取到token,阻止请求")
            return

        self.talk_message = self.send_plainTextEdit.toPlainText()
        if self.talk_message == "":
            QMessageBox.warning(self, "未输入提问消息", "提问消息不能为空,请正确输入提问消息！", QMessageBox.StandardButton.Ok)
            logger.warning(f"校验请求问题文字为空忽略")
            return

        self.send_plainTextEdit.clear()

        logger.info(f"准备发送请求")
        # 更新kwargs参数
        self.request_kwargs_slot()

        # 设置发送消息添加到布局
        sender_qvboxlayout = QHBoxLayout()  # 发送消息布局
        sender_image_label = QLabel()
        sender_image_label.setPixmap(QPixmap(":/main/user").scaled(50, 50))

        sender_text_QTextBrowser = QTextBrowser()
        sender_text_QTextBrowser.setText(self.talk_message)
        sender_text_QTextBrowser.setFontPointSize(14)  # 设置字体大小
        sender_text_QTextBrowser.setFixedHeight(sender_text_QTextBrowser.document().lineCount() * 33)

        sender_qvboxlayout.addWidget(sender_image_label)
        sender_qvboxlayout.addWidget(sender_text_QTextBrowser)
        self.scrollArea_v_layout.addLayout(sender_qvboxlayout)  # 添加发送消息到垂直布局

        # 连续对话开启,发送消息连续对话累计
        if not self.continuous_talk:
            self.talk_list = [{"role": "user", "content": f"{self.talk_message}"}]
        else:
            self.talk_list.append({"role": "user", "content": f"{self.talk_message}"})
        self.tread_send_message.talk_list = self.talk_list  # 替换子进程中的talk列表

        # 请求期间禁用对话框和发送按钮,并启动进度条
        self.send_shortcutkeys.activated.disconnect()

        # 开始刷新进度条并请求
        self.refresh_progressbar.flag = True
        self.refresh_progressbar.start()

        # 更新请求参数并发起请求
        self.tread_send_message.kwargs = self.request_kwargs
        self.tread_send_message.model = self.model_dialog.select_model
        self.tread_send_message.start()

        self.talk_scrollArea.update()
        self.talk_scrollArea_v_ScrollBar.setValue(self.talk_scrollArea_v_ScrollBar.maximum())
        self.talk_scrollArea_v_ScrollBar.setSliderPosition(self.talk_scrollArea_v_ScrollBar.maximum())  # 滚动到最大位置

    def receive_message_slot(self, robot_tetx: str):
        """
        请求消息信号回调槽函数
        """
        # 设置回答消息布局 添加到总布局
        robot_qvboxlayout = QHBoxLayout()  # 返回消息布局
        robot_image_label = QLabel()
        robot_image_label.setPixmap(QPixmap(":/main/robot").scaled(50, 50))
        robot_tetx_QTextBrowser = QTextBrowser()
        robot_tetx_QTextBrowser.setFontPointSize(14)  # 设置字体大小
        robot_tetx_QTextBrowser.setMarkdown(robot_tetx)
        line_count = robot_tetx_QTextBrowser.document().lineCount()
        if line_count == 1:
            robot_tetx_QTextBrowser.setFixedHeight(line_count * 35)
        elif line_count == 2:
            robot_tetx_QTextBrowser.setFixedHeight(line_count * 50)
        else:
            robot_tetx_QTextBrowser.setFixedHeight(line_count * 25)

        # 添加把返回文字添加到垂直布局,
        robot_qvboxlayout.addWidget(robot_image_label)
        robot_qvboxlayout.addWidget(robot_tetx_QTextBrowser)
        self.scrollArea_v_layout.addLayout(robot_qvboxlayout)

        # 恢复发送框的槽连接
        self.send_shortcutkeys.activated.connect(self.send_message_slot)

        # 连续对话未开启,返回消息连续对话累计
        if self.continuous_talk:
            self.talk_list.append({"role": "assistant", "content": f"{robot_tetx}"})

        # 进度条停止
        self.refresh_progressbar.flag = False
        self.talk_qprogressbar.setValue(100)

        # 焦点给到输入框方便连续输入
        self.send_plainTextEdit.setFocus()

        self.talk_scrollArea.update()
        self.talk_scrollArea_v_ScrollBar.setValue(self.talk_scrollArea_v_ScrollBar.maximum())
        self.talk_scrollArea_v_ScrollBar.setSliderPosition(self.talk_scrollArea_v_ScrollBar.maximum())  # 滚动到最大位置

    def closeEvent(self, event) -> None:
        """
        保证正常结束子线程
        """
        self.refresh_progressbar.flag = False
        self.thread_token.flag = False
        self.tread_send_message.flag = False

        self.refresh_progressbar.terminate()
        self.thread_token.terminate()
        self.tread_send_message.terminate()
        self.text_to_img_widget.close()

        super().closeEvent(event)

    def get_line_count(self, text_browser):
        """
        获取指定文本框有几行文本
        """
        document = text_browser.document()
        cursor = QTextCursor(document)
        cursor.movePosition(QTextCursor.End)
        line_count = cursor.blockCount()
        return line_count


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    window.show()

    sys.exit(app.exec())
